﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Diagnostics;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;

#if !WpfApp1
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Acap = Autodesk.AutoCAD.ApplicationServices.Application;
using static Autodesk.AutoCAD.Windows.SaveFileDialog;
using static Autodesk.AutoCAD.Windows.OpenFileDialog;
#endif

using JoinBox.WPF.多文档标签.ViewModel;
using NFox.WPF;
using Win32API;

namespace JoinBox.WPF.多文档标签.View
{
#if WpfApp1
    public class AutoDocs
    {
        /// <summary>
        /// 这个是所有程序都使用的与cad线程通讯
        /// </summary>
        public static JoinBox.BasalWindows.SyncContext2Form SyncContext2Form;
    }
#endif

    //此类将加载到一个同步上下文中
    public partial class DocWindow : UserControl
    {
        public RelayCommand<Info> CreateCmd { set; get; }
        public RelayCommand<Info> CloseCmd { set; get; }
        public RelayCommand<Info> TabCmd { set; get; }
        public RelayCommand<MouseButtonEventArgs> MidBtnEvent { set; get; }

        //与界面绑定的会通过这个类
        public DocumentTabData TabData;

        /// <summary>
        /// 文档标签的WPF
        /// </summary>
        /// <param name="acapIntermediary">必须传参进来,而且要在同步线程</param>
        public DocWindow()
        {
            //点击按钮就会触发命令
            TabData = new DocumentTabData();
            TabCmd = new RelayCommand<Info>((info) => {
                //选中按钮
                TabData.Info = info;
#if !WpfApp1
                AutoDocs.SyncManager.SyncContext.Post(() => {
                    var doc = info.Document;
                    if (doc != null && !doc.IsActive && !doc.IsDisposed)
                    {
                        Acap.DocumentManager.MdiActiveDocument = doc;
                    }
                });
#endif
            });

            CloseCmd = new RelayCommand<Info>((info) => {
                CloseDwg(info);
            });

            CreateCmd = new RelayCommand<Info>((info) => {
#if !WpfApp1
                //重置cad之后这两项的系统变量会弹窗,所以设置一下

                AutoDocs.SyncManager.SyncContext.Post(() => {
                    var docm = Acap.DocumentManager;
                    var doc = docm?.MdiActiveDocument;
                    if (doc != null)
                    {
                        CadSystem.Setvar("secureload", "0");  //AutoCAD2014版,加载lisp不警告
                        CadSystem.Setvar("acadlspasdoc", "1");//将acad.lsp文件加载到每一个图形
                    }
                    /*
                    发送消息到cad
                    出错原因:重置cad之后新建图纸就会报错
                    解决位置:<子类化-处理cad的新建图纸错误>
                    WinApi.SendMessage(Acap.MainWindow.Handle, (int)MsgType.WM_COMMAND, (IntPtr)18, IntPtr.Zero);   //qnew
                    WinApi.SendMessage(Acap.MainWindow.Handle, (int)MsgType.WM_COMMAND, (IntPtr)57600, IntPtr.Zero);//new
                    发送消息引起cad致命错误的概率很高*/
                });
                Qnew();
#endif
            });

            InitializeComponent();//这里弹错表示去项目文件取消迭代版本号
            DataContext = TabData;
            Loaded += DocWindow_Loaded;
        }

        private void DocWindow_Loaded(object sender, RoutedEventArgs e)
        {
#if WpfApp1
            for (int i = 0; i < 6; i++)
            {
                var a = new Info
                {
                    Document = new Document(),
                };
                TabData.Infos.Add(a);
            }
#else
            AutoLoadRun();
            Acap.DocumentManager.DocumentCreated += this.AutoLoadRun;

            //反应器->销毁前返回文档(先)
            Acap.DocumentManager.DocumentToBeDestroyed += DocumentManager_DocumentToBeDestroyed;
            //反应器->销毁前返回文档名(后)
            Acap.DocumentManager.DocumentDestroyed += DocumentManager_DocumentDestroyed;

            //反应器->否决命令
            Acap.DocumentManager.DocumentLockModeChanged += DocumentManager_DocumentLockModeChanged;
            //反应器->切换文档就事件激活
            Acap.DocumentManager.DocumentActivated += DocumentManager_DocumentActivated;

            //由于挤出的空间挡住了非最大化cad文档的空间,所以需要设置一下非最大化时候的文档.
            AutoDocs.SyncManager.SyncContext.Post(() => {
                DocSizeTool.TiaoZheng();
            });
#endif

        }


        /// <summary>
        /// 另存为之后立即关闭文档
        /// </summary>
        Document _IsCloseDocument = null;

        /// <summary>
        /// 关闭当前dwg
        /// </summary>
        /// <param name="doc"></param>
        private void CloseDwg(Info info)
        {
            //福萝卜用的是cad-com接口的saved可以判断是否已经保存
            string file = string.Empty;
            //数据库更改过但是没有保存过,就询问用户是否保存
            var save = MessageBoxResult.Cancel;

#if !WpfApp1
            Document doc = info.Document;

            AutoDocs.SyncManager.SyncContext.Post(() => {
                ReplaceActiveDocument(doc);

                file = doc.Database.Filename;

                // 如果数据库没有更改过,那么就直接关闭掉吧~
                // dbmod 用位码指示图形的修改状态:
                // 1.对象数据库被修改 4.数据库变量被修改 8.窗口被修改 16.视图被修改
                var dbmod = (DBmod)byte.Parse(CadSystem.Getvar("dbmod"));

                //仅缩放时候:数据库没有改变,但是变量变了,视图变了,就关掉好了
                var dbmodRecord = ((dbmod & DBmod.数据库有修改) != DBmod.数据库有修改) &&
                ((dbmod & DBmod.变量被修改) == DBmod.变量被修改) &&
                ((dbmod & DBmod.视图被修改) == DBmod.视图被修改);

                if (dbmod == DBmod.数据库冇修改 || dbmodRecord)
                {
                    doc.CloseAndDiscard();
                    return;
                }

                save = MessageBox.Show($"您已经修改过图纸,保存文档?\n\n{file}", "惊惊盒子", MessageBoxButton.YesNoCancel);

                if (save == MessageBoxResult.Cancel)
                    return;

                if (save == MessageBoxResult.No)
                {
                    try
                    {
                        //直接关闭不保存
                        doc.CloseAndDiscard();
                    }
                    catch
                    { }
                    return;
                }

                var ex = Path.GetExtension(file).ToLower();
                if (ex == ".dwg" || ex == ".dxf")
                {
                    if (!doc.IsReadOnly)//只读文件
                    {
                        doc.CloseAndSave(file);
                    }
                    else
                    {
                        SaveAs(info);
                    }
                }
                else
                {
                    _IsCloseDocument = info.Document;
                    Qsave(info);
                }

            });
#endif
        }


#if !WpfApp1
        /// <summary>
        /// 切换到当前文档
        /// </summary>
        /// <param name="doc"></param>
        private void ReplaceActiveDocument(Document doc)
        {
            // 切换到当前文档
            if (doc != null && doc != Acap.DocumentManager.MdiActiveDocument)
            {
                //如果不是当前文档,就切换过去,否则拿不到修改的变量
                Acap.DocumentManager.MdiActiveDocument = doc;
            }
        }

        /// <summary>
        /// 获取dwg名称
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        private string GetDocName(Document doc)
        {
            var file = doc.Database.Filename;

            var ex = Path.GetExtension(file).ToLower();
            //这里需要判断是否为只读
            if (ex == ".dwg" || ex == ".dxf")
            {
                file = Path.GetFileNameWithoutExtension(file);
            }
            else
            {
                file = Path.GetFileNameWithoutExtension(doc.Name);
            }
            return file;
        }

        /// <summary>
        /// 每次新建文档的自动执行
        /// </summary>
        public void AutoLoadRun(object sender = null, DocumentCollectionEventArgs e = null)
        {
            //这里的线程是在WPF线程下工作的,所以和cad交互部分我都会用_SyncContext2Form包裹
            //var name = Thread.CurrentThread.Name;
            //System.Windows.Forms.MessageBox.Show("AutoLoad_Thread::" + name);

            var infosLst = new List<Info>();

            foreach (Document doc in Acap.DocumentManager)
            {
                if (doc != null && !doc.IsDisposed)//doc.IsActive初始化时候异常
                {
                    var info = new Info
                    {
                        Document    = doc,
                        InfoDocName = GetDocName(doc),
                    };
                    infosLst.Add(info);
                }
            }

            Invoke(() => {
                //cad08这个事件可能会重复两次,所以只能清空再加入
                this.TabData.Infos.Clear();

                // 重建表格(防止tab占用Doc,所以加入名称)
                foreach (var Info in infosLst)
                {
                    this.TabData.Infos.Add(Info);
                }
            });

            //第一个文档不会调用激活事件,所以这里要写Pick标签
            if (e == null)
            {
                PickDocument();
            }
        }

        /// <summary>
        /// 选中当前活动文档的焦点
        /// </summary>
        /// <returns></returns>
        private Document PickDocument(Document doca = null)
        {
            Document pick = null;
            if (doca != null)
            {
                pick = doca;
            }
            else
            {
                //自运行一次时候,设置为激活的文档.
                foreach (Document doc in Acap.DocumentManager)
                {
                    if (doc != null && !doc.IsDisposed)
                    {
                        pick = doc;
                        break;
                    }
                }
            }
            BeginInvoke(() => {
                //新建了就激活为当前按钮
                TabData.InputPick = pick;
            });
            return pick;
        }

        /// <summary>
        /// 反应器->切换文档就事件激活
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DocumentManager_DocumentActivated(object sender = null, DocumentCollectionEventArgs e = null)
        {
            //切换文档标签激活
            if (e != null)
                PickDocument(e.Document);
        }

        /// <summary>
        /// 反应器->销毁前返回文档(先)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DocumentManager_DocumentToBeDestroyed(object sender, DocumentCollectionEventArgs e)
        {
            //操作WPF(界面)是要线程安全,操作cad不用
            BeginInvoke(() => {
                foreach (var info in this.TabData.Infos)
                {
                    if (info.Document == e.Document)
                    {
                        this.TabData.Infos.Remove(info);
                        break;
                    }
                }
            });
        }

        /// <summary>
        /// 反应器->销毁前返回文档名(后)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DocumentManager_DocumentDestroyed(object sender, DocumentDestroyedEventArgs e)
        {
            // 反应器->切换文档就事件激活也会设置焦点,所以此事件废弃.
            // 重设焦点
            // PickDocument();
        }

        const string QnewMethodstr = "QnewMethod";
        string[] CmdNewsDwgFile = { "NEW", "QNEW", "_NEW", "_QNEW" };
        /// <summary>
        /// 反应器->命令否决触发命令前(不可锁文档)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DocumentManager_DocumentLockModeChanged(object sender, DocumentLockModeChangedEventArgs e)
        {
            var up = e.GlobalCommandName.ToUpper();
            if (CmdNewsDwgFile.Contains(up))
            {
                //否决命令
                e.Veto();

                //如果直接调用了DocWindow.QnewMethod()可能会令否决失败,
                //因为用户可能和我一样有其他插件,而其他插件被加载时候会产生清空反应器的作用(反应器有计数超出不执行的情况).
                //所以要以发送异步命令到cad
                //SendStringToExecute第二个参数true时,命令必须有CommandFlags.Session,否则容易致命错误
                AutoDocs.SyncManager.SyncContext.Post(() => {
                    e.Document?.SendStringToExecute(QnewMethodstr + "\n", true, false, false);
                });
            }
            if (up == "#QSAVE" && _IsCloseDocument != null)
            {
                AutoDocs.SyncManager.SyncContext.Post(() => {
                    _IsCloseDocument.CloseAndDiscard();
                    _IsCloseDocument = null;
                });
            }

            if (up == "#QSAVE" || up == "#SAVEAS")
            {
                //执行了另存为之后,只能这里再刷新文档栏
                //因为这两个命令都有可能变成另存为,而另存为时候用户可能修改文档名,所以这里实现更改文档栏的名称
                foreach (var infoa in this.TabData.Infos)
                {
                    infoa.InfoDocName = GetDocName(infoa.Document);
                    infoa.Document    = infoa.Document;
                }
            }
        }

        /// <summary>
        /// 创建文档_自己写的版本_不可以改用发送消息版本,否则致命错误概率提高
        /// </summary>
        public void Qnew()
        {
            AutoDocs.SyncManager.SyncContext.Post(() => {
                QnewMethod();
            });
        }

        [CommandMethod(QnewMethodstr, CommandFlags.Modal | CommandFlags.Session)]
        public void QnewMethod()
        {
            // 这里补充注册表完整
            AcadRegistry.CreateDocumentsComplementation();

            var lastTemplate = CadSystem.Getvar("LastTemplate");//最后使用的模板
            var templatePath = CadSystem.Getvar("TemplatePath");//模板路径
            if (!File.Exists(lastTemplate))
            {
                lastTemplate = templatePath + "\\acad.dwt";
            }
            string openName;
#if true2
            var sa = new System.Windows.Forms.OpenFileDialog
            {
                Title            = "选择模板",
                Filter           = "模板文件(*.dwt)|*.dwt|所有文件(*.*)|*.*",
                FileName         = "acad.dwt",
                InitialDirectory = templatePath,//设置打开路径的目录
            };

            if (sa.ShowDialog() != System.Windows.Forms.DialogResult.OK)
            {
                return;
            }
            openName = sa.FileName;
#else
            var sa = new Autodesk.AutoCAD.Windows.OpenFileDialog("选择模板", lastTemplate, "dwt", "",
            OpenFileDialogFlags.AllowAnyExtension | OpenFileDialogFlags.ForceDefaultFolder);

            if (sa.ShowDialog() != System.Windows.Forms.DialogResult.OK)
            {
                return;
            }
            openName = sa.Filename;
#endif
            //System.Runtime.InteropServices.COMException:“内部应用程序出错。”
            //如果这里出现这个错误,是因为重置引起,
            //重置就会把注册表(和配置文件)删掉,复现这个错误.
            //而Acap.DocumentManager.Add需要其中的注册表,才不会报错
            //卡死因为没有CommandFlags.Session

            try
            {
                Acap.DocumentManager.Add(openName);
                //之后会进入激活文档事件 DocumentManager_DocumentActivated
            }
            catch
            { }
        }


        /// <summary>
        /// 打开_dwg
        /// </summary>
        public void OpenDwg()
        {
            //因为当前文档必然存在,所以直接发送命令就好了
            AutoDocs.SyncManager.SyncContext.Post(() => {
                var doc = Acap.DocumentManager.MdiActiveDocument;
                doc.SendStringToExecute("_Open\n", false, true, true); //不需要切换文档
            });
        }

        /// <summary>
        ///  保存_dwg
        /// </summary>
        public void Qsave(Info info)
        {
            //因为当前文档必然存在,所以直接发送命令就好了
            AutoDocs.SyncManager.SyncContext.Post(() => {
                info.Document?.SendStringToExecute("_Qsave\n", false, true, true); //不需要切换文档
            });
        }

        /// <summary>
        /// 另存为_dwg
        /// </summary>
        /// <param name="info"></param>
        void SaveAs(Info info)
        {
            //如果是只读状态,发送qsave是会提示只读,不会调用另存的,所以必须要一个另存为命令
            AutoDocs.SyncManager.SyncContext.Post(() => {
                info.Document?.SendStringToExecute("_Saveas\n", false, true, true); //不需要切换文档
            });
        }
#endif
        /// <summary>
        /// WPF界面线程安全
        /// </summary>
        /// <param name="ac"></param>
        public void BeginInvoke(Action ac)
        {
            //当前的WPF容器的线程安全
            this.Dispatcher.BeginInvoke(ac);
        }

        public void Invoke(Action ac)
        {
            //当前的WPF容器的线程安全
            this.Dispatcher.Invoke(ac);
        }


        /// <summary>
        /// 右键菜单
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
#if !WpfApp1
            if (sender is MenuItem me && me.DataContext is Info info)
            {
                switch (me.Header)
                {
                    case "新建dwg":
                        {
                            Qnew();
                        }
                        break;
                    case "打开dwg":
                        {
                            OpenDwg();
                        }
                        break;
                    /**********************/
                    case "保存":
                        {
                            Qsave(info);
                            //这是异步的,去拦截命令上面刷新一次
                        }
                        break;
                    case "另存":
                        {
                            SaveAs(info);
                            //这是异步的,去拦截命令上面刷新一次
                        }
                        break;
                    case "全部保存":
                        {
                            foreach (var infoa in this.TabData.Infos)
                            {
                                Qsave(infoa);
                            }
                        }
                        break;
                    /**********************/
                    case "关闭":
                        {
                            CloseDwg(info);
                        }
                        break;
                    case "关闭全部(会提示保存)":
                        {
                            foreach (var infoa in this.TabData.Infos)
                            {
                                CloseDwg(infoa);
                            }
                        }
                        break;
                    case "关闭全部(不保存)":
                        {
                            AutoDocs.SyncManager.SyncContext.Post(() => {
                                Acap.DocumentManager.CloseAll();
                            });
                        }
                        break;
                    case "关闭所有其他图纸(保存)":
                        {
                            foreach (var item in this.TabData.Infos)
                            {
                                if (item.Document != info.Document)
                                {
                                    CloseDwg(item);
                                }
                            }
                        }
                        break;
                    case "关闭所有其他图纸(不保存)":
                        {
                            AutoDocs.SyncManager.SyncContext.Post(() => {
                                foreach (Document doc in Acap.DocumentManager)
                                {
                                    if (info.Document != doc)
                                    {
                                        doc.CloseAndDiscard();
                                    }
                                }
                            });
                        }
                        break;
                    /**********************/
                    case "复制完整文件夹路径":
                        {
                            Clipboard.SetText(info.Document.Database.Filename);
                        }
                        break;
                    case "打开文件的位置":
                        {
                            //不重复打开dwg路径的资源管理器(高版本把没有保存过的设置为不可用按钮)
                            var db = info.Document.Database;
                            var ex = Path.GetExtension(db.Filename).ToLower();

                            //这里需要判断是否为只读
                            if (ex == ".dwg" || ex == ".dxf")
                            {
                                var wins = new ShellWindows();//这个名字是为了兼容高版本(没想到吧
                                if (wins.Enumerator.Count > 0)
                                {
                                    string dwgname = CadSystem.Getvar("dwgname");
                                    foreach (var item in wins.Enumerator)
                                    {
                                        if (item.LocationURL != null && item.LocationURL + "\\" + dwgname == db.Filename)
                                        {
                                            item.Quit();//关闭
                                        }
                                    }
                                }
                                //重开一个,防止选择状态被改变,
                                Process.Start("explorer", "/select,\"" + db.Filename + "\"");//加引号防止空格中断
                            }
                            else
                            {
                                AutoDocs.SyncManager.SyncContext.Post(() => {
                                    var ed = info.Document.Editor;
                                    ed.WriteMessage(Environment.NewLine + "你没有保存文件!\n");
                                });
                            }
                        }
                        break;
                }
            }
#endif

        }

        public double Leng(Point a, Point b)
        {
            return Math.Sqrt((b.X - a.X) * (b.X - a.X) + (b.Y - a.Y) * (b.Y - a.Y));
        }

        /// <summary>
        /// 鼠标按下事件
        /// 不要用MouseDown因为绑定了command之后就无法判断左键了,只能用PreviewMouseDown
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void RadioButton_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            //中键关闭文档
            if (e.MiddleButton == MouseButtonState.Pressed &&
                e.Source is RadioButton ra2 &&
                ra2.CommandParameter is Info info2)
            {
                CloseDwg(info2);
            }
        }


        Point oldPoint = new Point();
        bool canvas2_isMove = false;

        /// <summary>
        /// 鼠标左键按下(比命令绑定先运行)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void RadioButton_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            //if (e.LeftButton == MouseButtonState.Pressed &&
            //    e.Source is RadioButton ra1 &&
            //    ra1.CommandParameter is Info info1)
            //{

            //}

            //获取鼠标
            canvas2_isMove = true;
            oldPoint       = e.GetPosition(null);
        }


#if false
        /// <summary>
        /// 鼠标移动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void RadioButton_MouseMove(object sender, MouseEventArgs e)
        {
            Point p = Mouse.GetPosition(e.Source as FrameworkElement);
            Debug.WriteLine(sender);

            if (e.LeftButton != MouseButtonState.Pressed)//按着左键
            {
                return;
            }
            if (_JRadioButtonData == null)
            {
                return;
            }
            if (e.Source is RadioButton ra1 && ra1 != _JRadioButtonData.RadioButton)//不相等就是交换,但是按着左键的的时候不切换e.Source
            {
                //获取鼠标位置
                var X = e.GetPosition(ra1).X;
                var Y = e.GetPosition(ra1).Y;
                var pt = new Point(X, Y);

                //偏移量
                var offset = Math.Abs(_JRadioButtonData.Pt.X - pt.X);
                if (offset != 0)
                {
                    var Infos2 = this.TabData.Infos.ToList();
                    //记录选中文档按钮的位置,然后清理所有按钮
                    int number = 0;
                    for (int i = 0; i < this.TabData.Infos.Count - 1; i++)
                    {
                        if (this.TabData.Infos[i] == _JRadioButtonData.Info)
                        {
                            number = i;
                            break;
                        }
                    }
                    this.TabData.Infos.Clear();
                    if (offset > 0)  //往→
                    {
                        ++number;
                    }
                    else //往←
                    {
                        --number;
                    }

                    for (int i = 0; i < Infos2.Count - 1; i++)
                    {
                        if (i == number)
                        {
                            this.TabData.Infos.Add(_JRadioButtonData.Info);
                        }
                        else
                        {
                            this.TabData.Infos.Add(Infos2[i]);
                        }
                    }
                }
            }
        }
#endif

        //窗体_鼠标抬起
        private void Window_MouseUp(object sender, MouseButtonEventArgs e)
        {
            canvas2_isMove = false;
            //canvas2.Background = Brushes.Yellow;
        }

        //窗体_鼠标移动
        private void Window_MouseMove(object sender, MouseEventArgs e)
        {
            if (canvas2_isMove)
            {
                if (e.Source is RadioButton canvas2)
                {
                    //canvas2.Background = Brushes.Red;

#if false
                    var currEle = sender as FrameworkElement;//如果是窗体,那么这里会移动窗体

                    //通过鼠标来控制_约束移动方向
                    double xPos = e.GetPosition(null).X - oldPoint.X + (double)currEle.GetValue(Canvas.LeftProperty);
                    currEle.SetValue(Canvas.LeftProperty, xPos);

                    //double yPos = e.GetPosition(null).Y - oldPoint.Y + (double)currEle.GetValue(Canvas.TopProperty);
                    //currEle.SetValue(Canvas.TopProperty, yPos);
#else
                    //通过鼠标来控制_约束移动方向
                    double xPos = e.GetPosition(null).X - oldPoint.X + (double)canvas2.GetValue(Canvas.LeftProperty);
                    canvas2.SetValue(Canvas.LeftProperty, xPos);

                    //double yPos = e.GetPosition(null).Y - oldPoint.Y + (double)canvas2.GetValue(Canvas.TopProperty);
                    //canvas2.SetValue(Canvas.TopProperty, yPos);
#endif
                    oldPoint = e.GetPosition(null);
                }
            }
        }



    }
}
